#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/cdev.h>

#include "dev_info.h"


//设计一个类型，描述一个设备的信息
struct led_desc{
	unsigned int dev_major;//设备号
	struct class *cls;
	struct device *dev;//创建设备文件
	char *name;
};
struct led_desc *led_dev;  //声明一个设备对象，全局共享

//struct cdev cdev;

extern struct bus_type my_bus;

struct my_dev_desc my_drv_desc[LED_Quantity];

void my_led_on(int nr)
{
	if((nr > 0)&&(nr <= LED_Quantity))
	{
		writel(readl(my_drv_desc[nr-1].gp__dat) | 1 << my_drv_desc[nr-1].offset, my_drv_desc[nr-1].gp__dat);
	}
	else
	{
		printk("input error!\n");
	}
}

void my_led_off(int nr)
{
	if((nr > 0)&&(nr <= LED_Quantity))
	{
		writel(readl(my_drv_desc[nr-1].gp__dat) & ~(1 << my_drv_desc[nr-1].offset), my_drv_desc[nr-1].gp__dat);
	}
	else
	{
		printk("input error!\n");
	}
}

static int my_led_open(struct inode *inode, struct file *file)
{
	return 0;
}
	
static int my_led_release(struct inode *inode, struct file *file)
{
	return 0;
}
	
static long my_led_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	int nr;

	if(copy_from_user((void *)&nr, (void *)arg, sizeof(nr)))
		return -EFAULT;

	if (nr < 1 || nr > 4)
		return -EINVAL;

	switch (cmd) {
		case LED_ON:
			my_led_on(nr);
			break;
		case LED_OFF:
			my_led_off(nr);
			break;
		default:
			printk("Invalid argument");
			return -EINVAL;
	}

	return 0;
}

struct file_operations my_fops = {
	.owner = THIS_MODULE,
	.open = my_led_open,
	.release = my_led_release,
	.unlocked_ioctl = my_led_unlocked_ioctl,
};


int my_led_ioremap(struct my_dev_desc *pdesc)//接收device 信息 并且 对地址映射
{
	int ret,i;
	for(i = 0;i < LED_Quantity;i++){
		printk("[%d]-pdesc->num : %d\n",i+1,pdesc->num);
		my_drv_desc[i].num = pdesc->num;
		my_drv_desc[i].offset = pdesc->offset;
		
		my_drv_desc[i].gp__con = ioremap(pdesc->GP__CON, 4);
		if (my_drv_desc[i].gp__con == NULL) {
			printk("ioremap my_drv_desc[%d].gp__con\n",i+1);
			ret = -ENOMEM;
			return ret;
		}
		my_drv_desc[i].gp__dat = ioremap(pdesc->GP__DAT, 1);
		if (my_drv_desc[i].gp__dat == NULL) {
			printk("ioremap my_drv_desc[%d].gp__dat\n",i+1);
			ret = -ENOMEM;
			return ret;
		}		
		pdesc++;
	}
	return 0;
}

void my_led_io_init(void)
{
	int i;
	for(i=0;i<LED_Quantity;i++)
	{
		writel((readl(my_drv_desc[i].gp__con) & ~(0xf<< my_drv_desc[i].offset*4)) | (0x1<<my_drv_desc[i].offset*4), my_drv_desc[i].gp__con);
		writel(readl(my_drv_desc[i].gp__dat) & ~(0x1<<my_drv_desc[i].offset), my_drv_desc[i].gp__dat);
	}
}

void my_led_iounmap(void)		//注销映射地址
{
	int i;
	for(i=0;i<LED_Quantity;i++)
	{
		iounmap(my_drv_desc[i].gp__con);
		iounmap(my_drv_desc[i].gp__dat);
	}
}


int my_drv_probe(struct device *dev)
{
	int ret;
	struct my_dev_desc *pdesc;
	printk("__-----%s-----__\n",__FUNCTION__);
	//0.实例化全局的设备对象 --分配空间
	led_dev = kmalloc(sizeof(struct led_desc),GFP_KERNEL);
	if(led_dev==NULL)
	{
		printk(KERN_ERR "kmalloc error \n");
		return -ENOMEM;
	}
	//1.一般申请设备资源
	//申请设备号
	led_dev->name="led_dev_text";
	
	led_dev->dev_major = register_chrdev(0, led_dev->name, &my_fops);
	if(led_dev->dev_major < 0){
		printk(KERN_ERR"register_chrdev error !\n");
		ret = -ENODEV;
		goto err_0;
	}
	//2.自动创建一个设备文件
	led_dev->cls = class_create(THIS_MODULE,led_dev->name);//创建一个LED类
	if(IS_ERR(led_dev->cls))
	{
		printk(KERN_ERR"class_create error !\n");
		ret = PTR_ERR(led_dev->cls);//将指针出错的具体原因转化成一个出错码
		goto err_1;
	}
	led_dev->dev = device_create(led_dev->cls,NULL,MKDEV(led_dev->dev_major,0),NULL,"led%d",1);//创建LED设备文件节点
	if(IS_ERR(led_dev->dev))
	{
		printk(KERN_ERR"device_create error !\n");
		ret = PTR_ERR(led_dev->dev);//将指针出错的具体原因转化成一个出错码
		goto err_2;
	}
	pdesc = (struct my_dev_desc *)dev->platform_data;
	ret = my_led_ioremap(pdesc);
	if (ret < 0)
		goto err_3;
	my_led_io_init();
	printk("Led init\n");
	return 0;
err_3:
	device_destroy(led_dev->cls,MKDEV(led_dev->dev_major,0));
err_2:
	class_destroy(led_dev->cls);
err_1:
	unregister_chrdev(led_dev->dev_major, led_dev->name);
err_0:
	kfree(led_dev);
	return ret;
}


int my_drv_remove(struct device *dev)
{

	printk("__-----%s-----__\n",__FUNCTION__);
	//一般释放设备资源
	my_led_iounmap();
	//销毁创建了的设备节点
	device_destroy(led_dev->cls,MKDEV(led_dev->dev_major,0));
	class_destroy(led_dev->cls);	
	//取消申请了的设备号
	unregister_chrdev(led_dev->dev_major,led_dev->name);
	
	
	printk("Led exit\n");
	
	return 0;

}

//构建一个driver对象
struct device_driver my_drv = {
			.name = "my_dev_drv",
			.bus = &my_bus,
			.probe = my_drv_probe,
			.remove = my_drv_remove,
};

static int __init mydrv_init(void)
{
	//将driver注册到总线中
	int ret;
	printk("__-----%s-----__\n",__FUNCTION__);
	ret = driver_register(&my_drv);
	if(ret < 0)
	{
		printk("driver_register error !\n");
		return ret;
	}
	
	return 0;
}

static void __exit mydrv_exit(void)
{
	printk("__-----%s-----__\n",__FUNCTION__);
	//从总线中 注销driver 
	driver_unregister(&my_drv);
}


module_init(mydrv_init);
module_exit(mydrv_exit);
MODULE_LICENSE("GPL");



